home *** CD-ROM | disk | FTP | other *** search
- /* MacCalendar.c */
- /*
- * MacCalendar.c
- * Copyright © 1993-94 Apple Computer Inc. All rights reserved.
- * Based on the Status Bar Sample.c by Steve Christensen.
- *
- * File Type sdev
- * File Creator SCAL -- registered with DTS
- * Resource Type sdev
- * Resource ID 0
- * Resource Attributes purgeable
- *
- * Other options (MetroWerks, Think C 7.0):
- * Set Require Prototypes, Check Pointer Types, All other warnings.
- * Do not set trigraph recognition.
- * Enable Apple extensions.
- * MetroWerks link/project options:
- * Link single segment
- * Set Project type "Code Segment", Standard Header.
- */
- #ifndef SystemSevenOrLater
- #define SystemSevenOrLater 1
- #endif
-
- #include <Fonts.h>
- #include <Memory.h>
- #include <Menus.h>
- #include <Quickdraw.h>
- #include <Resources.h>
- #include <ToolUtils.h>
- #include <Types.h>
- #include <Windows.h>
- #include <Icons.h>
- #include "ControlStrip.h"
- #include "MacCalendar.h"
- /*
- * Metrowerks uses A4 to reference globals. The A4-setup code was copied from the
- * WDEF.c sample included in the Metrowerks DR3 distribution. MPW and Think C
- * use PC-relative addressing in a single-segment code module.
- */
- #ifdef __MWERKS__
- #include "A4Stuff.h" // also included in <MacHeaders>
- #include "SetupA4.h" // required to handle callback functions
- #endif
-
- /*
- * Define the patterns as C-strings so they can be addressed as constants
- * within the program.
- */
- #define kWhitePattern ((ConstPatternParam) "\000\000\000\000\000\000\000\000")
- #define kBlackPattern ((ConstPatternParam) "\377\377\377\377\377\377\377\377")
- #define pstrcpy(dst, src) (BlockMove((src), (dst), (src)[0] + 1))
- #define width(rect) ((rect).right - (rect).left)
- #define height(rect) ((rect).bottom - (rect).top)
-
- /*
- * This record defines the information we need to draw the calendar. It is initialized
- * when we are called with the sdevInitModule message, and passed to and from the
- * Status Bar manager.
- */
- typedef struct GlobalRecord {
- Handle iconSuite; /* Status bar icon */
- Handle textStrings; /* Balloon help string etc. */
- StringHandle dateStringHandle; /* The date string preference */
- PicHandle rightArrowPicture; /* Popup arrow */
- short fontNumber; /* Preference: display font */
- short fontSize; /* Preference: display font size */
- short firstDayOfWeek; /* Sunday == 1 */
- } GlobalRecord, *GlobalPtr, **GlobalHandle;
- /*
- * We access the global information through a macro. For example, GLOBAL.iconSuite is
- * the icon suite handle in the global record.
- */
- #define GLOBAL (*globalPtr)
- #define PicFrame(what) ((**GLOBAL.what).picFrame) /* The picture frame rectangle */
-
- /*
- * Define the local/global prototypes.
- */
- pascal long main(
- unsigned long message,
- GlobalHandle globalHandle,
- const Rect *statusRect,
- GrafPtr statusPort
- );
- long CtlStripInitialize(void);
- void CtlStripCleanUp(
- GlobalHandle globalHandle
- );
- long CtlStripPeriodicTickle(
- GlobalPtr globalPtr,
- const Rect *statusRect
- );
- long CtlStripMouseClick(
- GlobalPtr globalPtr,
- const Rect *statusRect
- );
- long CtlStripDrawStatusIcon(
- GlobalPtr globalPtr,
- const Rect *statusRect
- );
- OSErr CtlStripSavePreferences(
- GlobalPtr globalPtr
- );
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * main
- *
- * This "main" program is called by the Control Strip manager.
- */
- pascal long
- main(
- unsigned long message,
- GlobalHandle globalHandle,
- const Rect *statusRect,
- GrafPtr statusPort
- )
- {
- #ifdef __MWERKS__
- long oldA4 = SetCurrentA4();
- #endif
- short savedState;
- register GlobalPtr globalPtr;
- long result;
- Str255 helpString;
-
- if (0) { /* Unused variables */
- statusPort;
- }
- if (((long) globalHandle) != 0) {
- /*
- * We have already allocated the global record. Save its lock state, lock
- * the handle, and get the global pointer (so we can write GLOBAL.something)
- */
- savedState = HGetState((Handle) globalHandle);
- HLock((Handle) globalHandle);
- globalPtr = *globalHandle;
- }
- result = 0; /* Unknown message result */
- switch (message) {
- case sdevInitModule: /* Initialize the module */
- /*
- * Initialization always sets globalHandle to NULL to avoid the HSetState
- * at the exit routine. If CtlStripInitialize succeeds, it sets the result
- * to the global parameter.
- */
- globalHandle = NULL;
- result = CtlStripInitialize(); /* Do the initialization and */
- break; /* Return global or error code */
- case sdevCloseModule: /* Clean up before closing */
- CtlStripCleanUp(globalHandle);
- globalHandle = NULL;
- break;
- case sdevFeatures: /* Return feature bits */
- result = ( (1<<sdevWantMouseClicks) /* We handle mouse down */
- | (1<<sdevDontAutoTrack) /* We track the mouse, too */
- | (1<<sdevHasCustomHelp) /* Custom help string */
- );
- break;
- case sdevGetDisplayWidth: /* Return display width */
- result = kIconWidth + width(PicFrame(rightArrowPicture));
- break;
- case sdevPeriodicTickle: /* Nothing else is happening */
- result = CtlStripPeriodicTickle(globalPtr, statusRect);
- break;
- case sdevDrawStatus: /* Draw the status bar info */
- result = CtlStripDrawStatusIcon(globalPtr, statusRect);
- break;
- case sdevMouseClick: /* Status bar click */
- result = CtlStripMouseClick(globalPtr, statusRect);
- break;
- case sdevSaveSettings: /* Save changed settings */
- result = CtlStripSavePreferences(globalPtr);
- break;
- case sdevShowBalloonHelp: /* Display custom balloon help */
- /*
- * We don't really have a custom help string, but this shows how to do it.
- */
- SBGetDetachedIndString(helpString, GLOBAL.textStrings, kStringHelp);
- SBShowHelpString(statusRect, helpString);
- break;
- }
- if (globalHandle != NULL) /* If we have globals allocated */
- HSetState((Handle) globalHandle, savedState); /* Restore lock state */
- #ifdef __MWERKS__
- SetA4(oldA4);
- #endif
- return (result);
- }
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Initialization
- */
- long
- CtlStripInitialize(void)
- {
- register GlobalHandle globalHandle;
- register GlobalPtr globalPtr;
- long result;
- Str255 work;
- SavedSettingsHandle prefsHandle;
- Handle *theIconSuite;
- Boolean needSave;
- long tempLong;
- #define PREF (**prefsHandle)
-
- needSave = FALSE;
- globalHandle = (GlobalHandle) NewHandleClear(sizeof (GlobalRecord));
- result = noErr;
- if (globalHandle == NULL)
- result = MemError();
- else {
- HLock((Handle) globalHandle);
- globalPtr = *globalHandle;
- /*
- * Load and detach the icon suite
- */
- theIconSuite = &GLOBAL.iconSuite;
- result = SBGetDetachIconSuite(theIconSuite, ICON_StatusBar, svAllSmallData);
- }
- if (result == noErr) {
- GLOBAL.textStrings = GetResource('STR#', STRN_Info);
- result = ResError();
- }
- if (result == noErr) {
- DetachResource(GLOBAL.textStrings);
- GLOBAL.rightArrowPicture = GetPicture(PICT_RightArrow);
- if (GLOBAL.rightArrowPicture == NULL)
- result = ResError();
- }
- if (result == noErr) {
- DetachResource((Handle) GLOBAL.rightArrowPicture);
- /*
- * Get the saved preferences, if any, and configure the drawing
- * environment. Note that the sample status bar doesn't dispose
- * of the prefsHandle.
- */
- SBGetDetachedIndString(work, GLOBAL.textStrings, kStringPreference);
- result = SBLoadPreferences(work, (Handle *) &prefsHandle);
- if (result == noErr
- && GetHandleSize((Handle) prefsHandle) == sizeof (SavedSettings)
- && PREF.signature == kApplicationCreator
- && PREF.prefVersion == kPrefVersion) {
- /*
- * Use the saved preference resource
- */
- pstrcpy(work, PREF.fontName);
- GetFNum(work, &GLOBAL.fontNumber);
- GLOBAL.fontSize = PREF.fontSize;
- GLOBAL.firstDayOfWeek = PREF.firstDayOfWeek;
- pstrcpy(work, PREF.dateString); /* This must be last */
- }
- else {
- /*
- * Hmm, we don't have any preferences. Build a new preference resource.
- */
- result = noErr;
- SBGetDetachedIndString(work, GLOBAL.textStrings, kStringFontName);
- GetFNum(work, &GLOBAL.fontNumber);
- SBGetDetachedIndString(work, GLOBAL.textStrings, kStringFontSize);
- StringToNum(work, &tempLong);
- GLOBAL.fontSize = tempLong;
- SBGetDetachedIndString(work, GLOBAL.textStrings, kStringFirstDayOfWeek);
- StringToNum(work, &tempLong);
- GLOBAL.firstDayOfWeek = tempLong;
- /*
- * This must be last as we will store work in the dateStringHandle
- */
- SBGetDetachedIndString(work, GLOBAL.textStrings, kStringDayNames);
- needSave = TRUE;
- }
- GLOBAL.dateStringHandle = NewString(work);
- if (GLOBAL.dateStringHandle == NULL)
- result = MemError();
- }
- /*
- * We've finished all initialization. If there is an error, exit through
- * CtlStripCleanUp to dispose of handles and other junk. If initialization
- * are successful, unlock the handle and return the handle cast to a long.
- */
- if (result != noErr) {
- CtlStripCleanUp(globalHandle);
- result = 0;
- }
- else {
- if (needSave)
- (void) CtlStripSavePreferences(globalPtr);
- HUnlock((Handle) globalHandle);
- result = (long) globalHandle;
- }
- return (result);
- #undef PREF
- }
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Termination
- */
- void
- CtlStripCleanUp(
- GlobalHandle globalHandle
- )
- {
- register GlobalPtr globalPtr;
-
- if ((long) globalHandle > 0) {
- HLock((Handle) globalHandle);
- globalPtr = *globalHandle;
- if (GLOBAL.iconSuite != NULL)
- DisposeIconSuite(GLOBAL.iconSuite, TRUE);
- if (GLOBAL.textStrings != NULL)
- DisposeHandle(GLOBAL.textStrings);
- if (GLOBAL.rightArrowPicture != NULL)
- DisposeHandle((Handle) GLOBAL.rightArrowPicture);
- if (GLOBAL.dateStringHandle != NULL)
- DisposeHandle((Handle) GLOBAL.dateStringHandle);
- DisposeHandle((Handle) globalHandle);
- }
- }
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Draw the icon in the status bar.
- */
- long
- CtlStripDrawStatusIcon(
- GlobalPtr globalPtr,
- const Rect *statusRect
- )
- {
- Rect viewRect;
- short arrowHeight;
-
- viewRect = *statusRect;
- viewRect.right = viewRect.left + kIconWidth;
- (void) PlotIconSuite(&viewRect, atNone, ttNone, GLOBAL.iconSuite);
- /*
- * Draw an right-arrow to show that we have a popup menu. Well, we don't
- * actually have a popup menu, but we do pop up a calendar when clicked on.
- */
- arrowHeight = height(PicFrame(rightArrowPicture));
- viewRect.left = viewRect.right;
- viewRect.right += width(PicFrame(rightArrowPicture));
- viewRect.top += ((height(viewRect) - arrowHeight) >> 1);
- viewRect.bottom = viewRect.top + arrowHeight;
- DrawPicture(GLOBAL.rightArrowPicture, &viewRect);
- return (0);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Position the calendar with respect to the status bar. If there is enough room
- * above, put it above, else put it below. Left and right operate similarly.
- */
- static void
- GetDisplayRect(
- const Rect *statusRect,
- Point displaySize,
- Rect *windowRect
- )
- {
- Rect sBarRect;
-
- #define kCtlStripFrame 4 /* The frame above/below the icon itself */
- sBarRect = *statusRect;
- LocalToGlobal((Point *) &sBarRect.top);
- LocalToGlobal((Point *) &sBarRect.bottom);
- if (sBarRect.top - kCtlStripFrame - displaySize.v - (GetMBarHeight() + 2) > 0) {
- /*
- * The calendar is displayed above the status bar.
- */
- windowRect->bottom = sBarRect.top - kCtlStripFrame - 1;
- windowRect->top = windowRect->bottom - displaySize.v;
- }
- else {
- /*
- * The calendar is displayed below the status bar.
- */
- windowRect->top = sBarRect.bottom + kCtlStripFrame + 1;
- windowRect->bottom = windowRect->top + displaySize.v;
- }
- if (sBarRect.right - displaySize.h > 0) {
- /*
- * The calendar is displayed to the left of the calendar icon.
- */
- windowRect->right = sBarRect.right;
- windowRect->left = windowRect->right - displaySize.h;
- }
- else {
- /*
- * The calendar is displayed to the right of the calendar icon.
- */
- windowRect->left = sBarRect.left;
- windowRect->right = windowRect->left + displaySize.h;
- }
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Build the triangular "next month" and "previous month" buttons.
- */
- static void
- MakeTriangularButtons(
- const Rect *monthRect,
- PolyHandle *leftButton,
- PolyHandle *rightButton,
- Rect *leftButtonRect,
- Rect *rightButtonRect
- )
- {
- FontInfo fontInfo;
- short buttonSize;
- short halfSize;
- Rect bothButtonRect;
-
- GetFontInfo(&fontInfo);
- buttonSize = (fontInfo.ascent & ~1); /* Round down to even value */
- halfSize = buttonSize / 2;
- bothButtonRect = *monthRect;
- bothButtonRect.bottom -= (1 + fontInfo.leading);
- bothButtonRect.top = bothButtonRect.bottom - buttonSize;
- #define kButtonSeparation 4 /* 1.0d3, was 2 */
- bothButtonRect.left =
- (width(*monthRect) >> 1) - buttonSize - kButtonSeparation;
- bothButtonRect.right =
- (width(*monthRect) >> 1) + buttonSize + kButtonSeparation;
- *leftButtonRect = bothButtonRect;
- /* 1.0d4 + */
- leftButtonRect->right = leftButtonRect->left + halfSize;
- *rightButtonRect = bothButtonRect;
- rightButtonRect->left = rightButtonRect->right - halfSize;
- *leftButton = OpenPoly();
- MoveTo(halfSize, 0);
- LineTo(halfSize, buttonSize);
- LineTo(0, halfSize);
- LineTo(halfSize, 0);
- ClosePoly();
- OffsetPoly(*leftButton, leftButtonRect->left, leftButtonRect->top);
- *rightButton = OpenPoly();
- MoveTo(0, 0);
- LineTo(halfSize, halfSize);
- LineTo(0, buttonSize);
- LineTo(0, 0);
- ClosePoly();
- OffsetPoly(*rightButton, rightButtonRect->left, rightButtonRect->top);
- /* 1.0d4 - */
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Now that we've done setup, do the actual mouse tracking. If the mouse hits the
- * right button, advance the month and draw it. If it hits the left button, draw
- * the previous month.
- */
- static void
- DrawCalendarAndTrackMouse(
- GlobalPtr globalPtr,
- WindowPtr windowPtr,
- const Rect *monthRect
- )
- {
- Rect leftButtonRect;
- Rect rightButtonRect;
- PolyHandle leftButton;
- PolyHandle rightButton;
- unsigned long nowSeconds;
- DateTimeRec now;
- Point mousePt;
- short thisYear;
- short thisMonth;
- typedef enum {
- inNoButton = 0,
- inLeftButton = 1,
- inRightButton = 2
- } WhichButton;
- WhichButton inButton;
- WhichButton wasInButton;
- unsigned long nextMonthTick;
- Boolean redrawButtons;
- Str255 dateString;
-
- FrameRect(monthRect);
- MakeTriangularButtons(
- monthRect,
- &leftButton,
- &rightButton,
- &leftButtonRect,
- &rightButtonRect
- );
- pstrcpy(dateString, *GLOBAL.dateStringHandle);
- GetDateTime(&nowSeconds);
- Secs2Date(nowSeconds, &now);
- inButton = wasInButton = inNoButton;
- thisYear = thisMonth = 0;
- InitCursor();
- while (WaitMouseUp()) {
- if (thisYear != now.year || thisMonth != now.month) {
- /*
- * Draw the new month. Also make sure the buttons are drawn.
- */
- redrawButtons = TRUE;
- EraseRect(&windowPtr->portRect);
- FrameRect(monthRect);
- switch (wasInButton) {
- case inLeftButton:
- FillPoly(leftButton, kWhitePattern);
- break;
- case inRightButton:
- FillPoly(rightButton, kWhitePattern);
- break;
- }
- FramePoly(leftButton);
- FramePoly(rightButton);
- DrawCalendar(
- now.year,
- now.month,
- GLOBAL.firstDayOfWeek, /* Sunday */
- dateString,
- &windowPtr->portRect,
- GLOBAL.fontNumber,
- GLOBAL.fontSize
- );
- thisYear = now.year;
- thisMonth = now.month;
- } /* If drawing new month */
- /*
- * Get the mouse and track it while it is in one of our buttons
- */
- GetMouse(&mousePt);
- if (PtInRect(mousePt, &leftButtonRect))
- inButton = inLeftButton;
- else if (PtInRect(mousePt, &rightButtonRect))
- inButton = inRightButton;
- else {
- inButton = inNoButton;
- }
- if (redrawButtons || inButton != wasInButton) {
- switch (wasInButton) {
- case inLeftButton: FillPoly(leftButton, kWhitePattern); break;
- case inRightButton: FillPoly(rightButton, kWhitePattern); break;
- }
- switch (inButton) {
- case inLeftButton: FillPoly(leftButton, kBlackPattern); break;
- case inRightButton: FillPoly(rightButton, kBlackPattern); break;
- }
- FramePoly(leftButton);
- FramePoly(rightButton);
- if (inButton != wasInButton && inButton != inNoButton)
- nextMonthTick = 0; /* Force new month drawing */
- redrawButtons = FALSE;
- wasInButton = inButton;
- } /* If button click change */
- if (inButton != inNoButton && TickCount() > nextMonthTick) {
- /*
- * The user has clicked in a button, or has held the mouse
- * down in a button for one second. Draw the appropriate month.
- */
- nextMonthTick = TickCount() + 60;
- switch (inButton) {
- case inLeftButton:
- if (--now.month <= 0) { /* Previous month or year */
- now.month = 12;
- --now.year;
- }
- break;
- case inRightButton:
- if (++now.month > 12) { /* Next month or year */
- now.month = 1;
- ++now.year;
- }
- break;
- } /* Which button was clicked */
- } /* Moving to a new month */
- } /* Loop while mouse down */
- KillPoly(leftButton);
- KillPoly(rightButton);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Click in the status bar icon
- */
- long
- CtlStripMouseClick(
- GlobalPtr globalPtr,
- const Rect *statusRect
- )
- {
- Rect windowRect;
- WindowPtr windowPtr;
- GrafPtr savePort;
- Point displaySize;
- Rect monthRect;
-
- displaySize = GetCalendarDisplaySize(GLOBAL.fontNumber, GLOBAL.fontSize);
- displaySize.h += 2;
- displaySize.v += 2;
- GetDisplayRect(statusRect, displaySize, &windowRect);
- windowPtr = NewWindow(
- NULL,
- &windowRect,
- "\p",
- TRUE,
- plainDBox,
- (WindowPtr) -1L,
- FALSE, /* No go-away box */
- 0 /* No refCon */
- );
- if (windowPtr != NULL) {
- GetPort(&savePort);
- SetPort(windowPtr);
- GetCalendarMonthRect(
- GLOBAL.fontNumber,
- GLOBAL.fontSize,
- &windowPtr->portRect,
- &monthRect
- );
- if (StillDown()) {
- /*
- * Design the two triangular buttons that will be displayed on the
- * bottom line of the calendar and create the polygons. Then draw
- * the calendar and track the mouse while it's held down.
- */
- DrawCalendarAndTrackMouse(
- globalPtr,
- windowPtr,
- &monthRect
- );
- }
- SetPort(savePort);
- DisposeWindow(windowPtr);
- }
- return (0);
- }
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Save current status for restarts.
- */
- OSErr
- CtlStripSavePreferences(
- GlobalPtr globalPtr
- )
- {
- OSErr status;
- SavedSettingsHandle prefsHandle;
- Str255 work;
- #define PREF (**prefsHandle)
-
- prefsHandle = (SavedSettingsHandle) NewHandleClear(sizeof (SavedSettings));
- status = MemError();
- if (status == noErr) {
- HLock((Handle) prefsHandle);
- PREF.signature = kApplicationCreator;
- PREF.prefVersion = kPrefVersion;
- GetFontName(GLOBAL.fontNumber, PREF.fontName);
- PREF.fontSize = GLOBAL.fontSize;
- pstrcpy(PREF.dateString, *GLOBAL.dateStringHandle);
- SBGetDetachedIndString(work, GLOBAL.textStrings, kStringPreference);
- status = SBSavePreferences(work, (Handle) prefsHandle);
- DisposeHandle((Handle) prefsHandle);
- }
- return(status);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * We don't do anything interesting for periodic tickles.
- */
- long
- CtlStripPeriodicTickle(
- GlobalPtr globalPtr,
- const Rect *statusRect
- )
- {
- if (0) { /* Unused variables */
- globalPtr;
- statusRect;
- }
- return (0);
- }
-